今天要介紹的是 Iterator 模式,我們就直接用例子來解釋,假設到了一間餐廳,服務員會來做接待,並且將菜單拿出來,服務員會將上面的菜色都先唸過一遍給客人知道。如果今天餐廳剛好牛排都賣完了,就不能再跟客人說還有牛排可以點,這時候我們會有兩種選擇。
第一種是跟服務員說今天牛排沒了,等一下看到菜單上有牛排的部分就不要說出來,我們想想看服務員除了要做很多事情外,還要記得不能將牛排說出來,這樣真的不是好方法,也有可能會忘記。那我們試試看第二種方式,直接在菜單上把牛排去除掉(或者劃掉),這樣服務員只需要照著菜單上有的餐點說出來就好,也簡單明瞭,更不怕會忘記。
也就是說,我們將菜單與服務員做解耦合,服務員要唸出所有餐點時,只需要知道照著菜單上有的餐點唸出來就好,而不是將餐點記在自己的腦中,每當有餐點賣完時就要去改變腦中的餐點邏輯。希望這樣解釋大家能夠理解,那我們直接來看範例吧!
提供一種方法順序訪問一個聚合對象中各個元素,而又無須暴露該對象的內部表示。
(圖片來源:https://www.wikiwand.com/en/Iterator_pattern#Media/File:Iterator_UML_class_diagram.svg)
Menu
會去繼承自IAggregate
,代表菜單這個類別需要去實現迭代的功能,並且會在裡面回傳指定的Iterator
實體。MenuIterator
即為Menu
指定的Iterator
實體,會去實作迭代功能,將菜單裡所有的餐點都輪播一遍。using System;
using System.Collections.Generic;
namespace DAY28_Iterator
{
internal class Program
{
static void Main(string[] args)
{
Menu Menu = new Menu();
Menu.AddMeal(new Meal("牛排"));
Menu.AddMeal(new Meal("漢堡"));
Menu.AddMeal(new Meal("雞排"));
Menu.AddMeal(new Meal("義大利麵"));
Iterator iterator = Menu.Iterator();
Console.WriteLine("菜單:");
while (iterator.HasNext())
{
Meal meal = (Meal)iterator.Next();
Console.WriteLine($"- {meal.GetMeal()}");
}
}
}
// 定義總計介面,需要執行迭代方法
public interface IAggregate
{
public abstract Iterator Iterator();
}
public interface Iterator
{
// 確認是否有下個元素
public abstract bool HasNext();
// 執行下個元素
public abstract object Next();
}
public class Meal
{
private string _name;
public Meal(string name)
{
_name = name;
}
public string GetMeal()
{
return _name;
}
}
// 需要實作 IAggregate,有迭代方法去列出菜單裡的內容
public class Menu : IAggregate
{
private List<Meal> _meals;
public Menu()
{
_meals = new List<Meal>();
}
public Meal GetMealIndex(int index)
{
return _meals[index];
}
public void AddMeal(Meal meal)
{
_meals.Add(meal);
}
public int GetLength()
{
return _meals.Count;
}
public Iterator Iterator()
{
return new MenuIterator(this);
}
}
// 屬於 Menu 的 Iterator
public class MenuIterator : Iterator
{
private Menu _menu;
private int _index;
public MenuIterator(Menu Menu)
{
_menu = Menu;
_index = 0;
}
public bool HasNext()
{
if (_index < _menu.GetLength())
return true;
else
return false;
}
public object Next()
{
Meal meal = _menu.GetMealIndex(_index);
_index++;
return meal;
}
}
}
Iterator 模式就是將迭代行為以及物件本身的實作細節分開,我們從範例中可以看到,在Main
裡面去呼叫菜單後,只需要去判端HasNext()
後,再去使用Next()
方法即可將菜單裡所有餐點都輪播一遍。
如果我們今天要給菜單增加一個特別的邏輯,可能是要增加判斷說假日的話餐點都要做一些額外調整,但平日就一樣保持正常的邏輯,那們可以先增加一個HolidayMenuIterator
, 在裡面去做專屬於假日時的邏輯判斷,接著將Menu
類別新增建構子,在建構時將指定的Iterator
傳入,並且回傳這個Iterator
。這樣我們就可以讓服務生不用知道實際的邏輯細節,只需要照著菜單有的東西去做輪播,並且還能夠隨時的去變換菜單餐點迭代的邏輯!